



#import "H264HwDecoderImpl.h"
@interface H264HwDecoderImpl()
{
    uint8_t *_sps;
    NSInteger _spsSize;
    uint8_t *_pps;
    NSInteger _ppsSize;
    VTDecompressionSessionRef _deocderSession;
    CMVideoFormatDescriptionRef _decoderFormatDescription;
//    VTDecompressionSessionCanAcceptFormatDescription
}
@end


@implementation H264HwDecoderImpl
//解码回调函数
static void didDecompress( void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration )
{
    CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
    *outputPixelBuffer = CVPixelBufferRetain(pixelBuffer);
    H264HwDecoderImpl *decoder = (__bridge H264HwDecoderImpl *)decompressionOutputRefCon;
    if (decoder.delegate!=nil)
    {
        [decoder.delegate displayDecodedFrame:pixelBuffer];
    }
}


-(BOOL)initH264Decoder {
    
    if(_deocderSession) {
        return YES;
    }
    NSLog(@"初始化解码器kkkkkkkkkkkkk");
    const uint8_t* const parameterSetPointers[2] = { _sps, _pps };
    const size_t parameterSetSizes[2] = { _spsSize, _ppsSize };
    OSStatus status;
    status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
                                                                          2, //param count
                                                                         (const uint8_t *const*) parameterSetPointers,
                                                                          parameterSetSizes,
                                                                          4, //nal start code size
                                                                          &_decoderFormatDescription);
    
    if(status == noErr) {
        
        NSDictionary *destinationPixelBufferAttributes = @{
                                                           (id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange],
                                                           };
        
        /*[NSNumber numberWithBool:YES]:(id)kCVPixelBufferOpenGLESCompatibilityKey*/
        VTDecompressionOutputCallbackRecord callBackRecord;
        callBackRecord.decompressionOutputCallback = didDecompress;
        callBackRecord.decompressionOutputRefCon = (__bridge void *)self;
        
        status = VTDecompressionSessionCreate(kCFAllocatorDefault,_decoderFormatDescription,NULL,(__bridge CFDictionaryRef)destinationPixelBufferAttributes,&callBackRecord,&_deocderSession);
        
//                VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_ThreadCount, (__bridge CFTypeRef)[NSNumber numberWithInt:1]);
//        
                VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
        if (status) {
            NSLog(@"IOS8VT: VTDecompressionSessionCreate failed status=%d", (int)status);
        }
        
    } else {
        NSLog(@"IOS8VT: reset decoder session failed status=%d", (int)status);
    }
    
    return YES;
}

-(CVPixelBufferRef)decode:(uint8_t *)frame withSize:(uint32_t)frameSize
{
    CVPixelBufferRef outputPixelBuffer = NULL;
    if (_deocderSession) {
        CMBlockBufferRef blockBuffer = NULL;
        
        OSStatus status  = CMBlockBufferCreateWithMemoryBlock(NULL,
                                                              (void *)frame,
                                                              frameSize,
                                                              kCFAllocatorNull,
                                                              NULL,
                                                              0,
                                                              frameSize,
                                                              FALSE,
                                                              &blockBuffer);
        if(status == kCMBlockBufferNoErr) {
            CMSampleBufferRef sampleBuffer = NULL;
            const size_t sampleSizeArray[] = {frameSize};
            //NULL好像就是cmtime时间类的参数
            status = CMSampleBufferCreateReady(kCFAllocatorDefault,
                                               blockBuffer,
                                               _decoderFormatDescription ,
                                               1, 0, NULL, 1, sampleSizeArray,
                                               &sampleBuffer);
            
            if (status == kCMBlockBufferNoErr && sampleBuffer) {
                VTDecodeFrameFlags flags = 0;
                VTDecodeInfoFlags flagOut = 0;
                
                OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(_deocderSession,
                                                                          sampleBuffer,
                                                                          flags,
                                                                          &outputPixelBuffer,
                                                                          &flagOut);
                if(decodeStatus == kVTInvalidSessionErr) {
                    NSLog(@"IOS8VT: Invalid session, reset decoder session");
                } else if(decodeStatus == kVTVideoDecoderBadDataErr) {
                    NSLog(@"IOS8VT: decode failed status badData = %d(Bad data)", (int)decodeStatus);
                } else if(decodeStatus != noErr) {
                    
//                    NSLog(@"IOS8VT: decode failed status = %d", (int)decodeStatus);
                    
                }
                CFRelease(sampleBuffer);
            }
            CFRelease(blockBuffer);
        }

    }
    return outputPixelBuffer;
}

#pragma mark - 对外提供解码接口
-(void) decodeNalu:(uint8_t *)frame withSize:(uint32_t)frameSize
{
//    NSLog(@">>>>>>>>>>开始解码");
    int nalu_type = (frame[4] & 0x1F);
    CVPixelBufferRef pixelBuffer = NULL;
    uint32_t nalSize = (uint32_t)(frameSize - 4);
    uint8_t *pNalSize = (uint8_t*)(&nalSize);
    frame[0] = *(pNalSize + 3);
    frame[1] = *(pNalSize + 2);
    frame[2] = *(pNalSize + 1);
    frame[3] = *(pNalSize);
    
    switch (nalu_type)
    {
        case 0x05:
            if([self initH264Decoder])
            {
                pixelBuffer = [self decode:frame withSize:frameSize];
            }
            break;
        case 0x07:
            if (_sps) {
                break;
            }
            _spsSize = frameSize - 4;
            
//            if (_sps) {
//                free(_sps);
//                _sps = NULL;
//            }
            _sps = malloc(_spsSize);
            memcpy(_sps, &frame[4], _spsSize);
            break;
        case 0x08:
        {
            if (_pps) {
                break;
            }
            _ppsSize = frameSize - 4;
//            if (_pps) {
//                free(_pps);
//                _pps = NULL;
//            }
            _pps = malloc(_ppsSize);
            memcpy(_pps, &frame[4], _ppsSize);
            break;
        }
        default:
        {
            pixelBuffer = [self decode:frame withSize:frameSize];
            break;
        }
    }
}
-(void)decStop{
    
    if (_decoderFormatDescription) {
        CFRelease(_decoderFormatDescription);
        _decoderFormatDescription = NULL;
    }
    
    if (_deocderSession) {
        VTDecompressionSessionInvalidate(_deocderSession);
        CFRelease(_deocderSession);
        _deocderSession = NULL;
    }
    
    if (_sps) {
        free(_sps);
        _sps = NULL;
    }
    
    if (_pps) {
        free(_pps);
        _pps = NULL;
    }
    _spsSize = 0;
    _ppsSize = 0;
    
}

@end
